{% extends 'universal/myself_base.html' %}
{% from 'universal/macro.html' import pagination_show %}{# 导入宏，格式是from 宏所在文件名 import 宏名 #}
{% block title %}所有文章{% endblock %}
{% block page_content %}
    {% for i,j in d.items() %}{# d是字典，items()方法返回可遍历的键和值，键是文章对象，值是对应文章的发布时间戳(传来时已由数据库里的utc时间加了8小时) #}
        <div class="media"><!--复制的https://v3.bootcss.com/components/#media，并添加了头像图片的宽高(因链接里示意图里写了宽高)，把标题放在a标签里，用div添加了一种发布时间，用p标签添加了作者(放在a标签里)和另一种发布时间和收藏点赞标签(标签是bootstrap的标签里放字体图标，且给标签强制添加了自己定义的类名和url属性，添加的原因在标签代码后的注释里)和阅读量标签和文章类型标签，用div标签添加了内容-->
            <div class="media-left">
                <a href="{{ url_for('article.search_by_user') }}?user_id={{ i.uid }}">
                    <img class="media-object" src="{{ url_for('static',filename='upload/'+i.user.portrait_file_name) }}" alt="portrait" style="width:64px;height:64px;">{# i.user的注释在下面 #}
                </a>
            </div>
            <div class="media-body">
                <div style="float:right;color:silver;">{{ moment(i.timestamp).fromNow() }}</div><!--写在标题前面，并设置向右飘，这样会与标题显示在同一行。moment已在父级模板中导入。最好moment()里传utc时间戳。-->
                <h4 class="media-heading"><a href="{{ url_for('article.detail') }}?aid={{ i.id }}">{{ i.title }}</a></h4>
                <p>
                    <a href="{{ url_for('article.search_by_user') }}?user_id={{ i.uid }}" style="color:inherit;">{{ i.user.username }}</a>{# .user是user表中根据文章反向找作者时添加的反向引用，再.username就是作者名 #}<!--inherit使元素呈现出它上一级的颜色(不改的话a标签默认蓝色)，-->
                    <span style="color:lightslategray;">{{ j.strftime('%Y-%m-%d %H:%M:%S') }}</span><!--指定时间格式，不想显示数据库中时间里的小数。用p标签将作者和时间放在同一行，但若要对时间加样式，需把时间放在span标签里，放在div里时不会与作者显示在同一行-->
                    <span class="label label-info collect_span" url="{{ url_for('article.collected_or_liked',article_id=i.id) }}"><!--添加自己定义的类名collect_span的原因在click()事件的注释里。这里发送Ajax请求时，请求地址中需传入参数article_id，Ajax代码里无法构造带参数的路由(但仍然可以将参数传给路由，如detail.html中写法，和register.html中一样)，所以在这里构造好-->
                        {% if current_user.is_authenticated and current_user.is_collected_or_liked(i.id) %}{# 已登录且已收藏才显示取消收藏，未登录或未收藏都显示收藏。判断是否登录只能写在前面，因写在后面时，若未登录，会先获取is_collected_or_liked()，不登录就获取不到所以报错，所以把判断是否登录写前面，未登录时直接逻辑短路，不再执行and后的条件判断 #}
                            <span class="glyphicon glyphicon-star" aria-hidden="true" id="collect_icon" tag="1">取消收藏{{ i.collect_times }}</span><!--tag用来获取当前span标签的文字内容(不含后面的变量的值)，因文字内容无法直接用.text()获得(它会连带获得后面的变量的值，即便在变量外加个span标签也是)，所以用tag来代替文字内容。也可以在文字内容外加个span标签，再通过这个标签获取文字内容-->
                        {% else %}
                            <span class="glyphicon glyphicon-star-empty" aria-hidden="true" id="collect_icon" tag="0">收藏{{ i.collect_times }}</span>
                        {% endif %}
                    </span>
                    &nbsp;<!--故意加的空格，作两个标签的间隔-->
                    <span class="label label-info like_span" url="{{ url_for('article.collected_or_liked',article_id=i.id) }}"><!--这里发送Ajax请求时，请求地址中需传入参数article_id，Ajax代码里无法构造带参数的路由，所以在这里构造好-->
                        {% if current_user.is_authenticated and current_user.is_collected_or_liked(i.id) %}
                            <span class="glyphicon glyphicon-heart" aria-hidden="true" id="like_icon" tag="1">取消点赞{{ i.star_times }}</span>
                        {% else %}
                            <span class="glyphicon glyphicon-heart-empty" aria-hidden="true" id="like_icon" tag="0">点赞{{ i.star_times }}</span>
                        {% endif %}
                    </span>
                    &nbsp;<!--故意加的空格，作两个标签的间隔-->
                    <span class="label label-default">阅读量{{ i.click_times }}</span>
                    &nbsp;<!--故意加的空格，作两个标签的间隔-->
                    <span class="label label-default">{{ i.article_type.type_name }}</span><!--文章类型-->
                </p>
                <div>{{ i.content | safe | myself_trim }}</div>{# 变量名 | 过滤器(*args) #}
                {#
                过滤器其实是函数，根据需要传参，可以链式调用，即连着写多个|。模板引擎自带一些过滤器，常见的有
                safe:禁止模版引擎转义字符，将传来的字符原样交给浏览器渲染。如传给html文件一个<h1>a</h1>，不写safe时模板引擎会将尖括号转为对应的字符实体，浏览器渲染时无法显示成一级标题的样式；加了safe后模板引擎不转义传来的字符，浏览器渲染时能显示一级标题的样式
                capitalize:首字母转为大写
                lower和upper:全转为小写或大写
                title:一句话中每个单词的首字母转为大写
                reverse:反转字符串
                format:格式化字符串。如{{ '%s is %d years old.' | format('xiaoming',18) }}输出xiaoming is 18 years old.
                truncate:截断字符串，点击查看源码
                针对列表的过滤器first、last、length、sum(需列表元素是数字)、sort
                针对字典的过滤器，不用写|，直接字典名.keys()获取所有键，字典名.values()获取所有值，字典名.items()获取所有键值对

                自定义过滤器和内置的重名时，会覆盖内置的
                方式一，先定义函数，再给app添加过滤器，最后在模板文件中使用
                def str_replace(string): # 将字符串中的hello替换为hEllo
                    string=string.replace('hello','hEllo') # 也可以用正则
                    return string # 需返回操作后的结果
                app.add_template_filter(str_replace,'str_replace') # 第一个参数是绑定的函数名，第二个参数是外界(模板文件中)使用的名字
                模板文件中|后写str_replace就是用这个自定义过滤器，会将|前的变量传入过滤器函数，页面上显示的是返回的结果

                方式二，先定义函数，再用装饰器装饰，最后在模板文件中使用
                @app.app_template_filter('list_reverse') # @后是对象名，可以是蓝本对象名，参数是外界(模板文件中)使用的名字
                def list_reverse(l): # 反转列表
                    l=list(l) # 将传入的变量转为list，防止传入非list类型
                    l=l.reverse()
                    return l
                模板文件中|后写list_reverse就是用这个自定义过滤器，会将|前的变量传入过滤器函数，页面上显示的是返回的结果
                #}
            </div>
        </div>
    {% endfor %}
    {% if pagination.items %}{# 没有文章时不显示分页导航条，不加的话没有文章时会显示上/下一页按钮 #}
        {{ pagination_show(pagination,'article.all_article') }}{# 调用宏，显示分页导航条 #}
        {# {{ pagination_show(pagination,'article.all_article',id=10,name='xiao') }}若需要传递额外参数，可以直接写在宏的后面，会自动显示在浏览器地址栏 #}
    {% endif %}
{% endblock %}
{% block scripts %}
    {{ super() }}
    <script>
        //点击收藏后，收藏变为取消收藏，刷新页面，点赞也变为取消点赞(但刚才没手动点击点赞)，反之亦然。原因是前端页面虽然显示收藏按钮和点赞按钮，但点击时都执行同一个方法(is_collected_or_liked)，没做区分，所以收藏的同时也会点赞，反之亦然，即收藏就是点赞，点赞就是收藏
        $(function (){
            $('.collect_span').click(function (){ //根据类名获取元素(由于收藏和点赞的bootstrap标签的类名一样，获取元素时为了区分，手动加了自定义类名)，而不是根据id，因页面内的所有收藏按钮的id都一样(若已设置id的话，由于整体内容在for循环里，所以一样)，若根据id获取，只会获取到第一个收藏按钮，后面那些收藏按钮获取不到，导致后面那些收藏按钮无法触发点击事件
                let _this=$(this) //和上一行中的注释类似，Ajax请求的回调函数里不能根据id获取元素，页面内的所有收藏按钮的id都一样(若已设置id的话)，所以用临时变量保存this，这样回调函数里用_this能确保获取到当前点击的对象，即要改样式的对象(忽略代码里实际当前点击的对象和要改样式的对象是上下级关系)
                let tag=_this.children('span').attr('tag') //定义tag为1表示数据库里已收藏，此时按钮显示取消收藏，点击后变成收藏，图标变成空心
                $.post(_this.attr('url'),function (data){ //发送Ajax请求，同register.html中。attr()内只写属性是获取属性的值，写属性和值是设置属性的值，attr()在1.8.3版本没问题。补充function()是发送Ajax请求后执行的回调函数
                    if(tag==0){
                        _this.children('span').removeClass() //清除原来的class，因addClass()不会清除原来的class。不能直接放在click()后面，即发送Ajax请求之前，放在那里点击时样式的变化有延时
                        _this.children('span').addClass('glyphicon glyphicon-star')
                        _this.children('span').text('取消收藏'+data.number)
                        _this.children('span').attr('tag','1') //不要忘了手动改tag，否则连续点击按钮，且不刷新页面时，按钮内容显示得不正确
                    }else {
                        _this.children('span').removeClass()
                        _this.children('span').addClass('glyphicon glyphicon-star-empty')
                        _this.children('span').text('收藏'+data.number)
                        _this.children('span').attr('tag','0')
                    }
                })
            })
            $('.like_span').click(function (){
                let _this=$(this)
                let tag=_this.children('span').attr('tag') //定义tag为1表示数据库里已点赞，此时按钮显示取消点赞，点击后变成点赞，图标变成空心
                $.post(_this.attr('url'),function (data){
                    if(tag==0){
                        _this.children('span').removeClass()
                        _this.children('span').addClass('glyphicon glyphicon-heart')
                        _this.children('span').text('取消点赞'+data.number)
                        _this.children('span').attr('tag','1') //不要忘了手动改tag，否则连续点击按钮，且不刷新页面时，按钮内容显示得不正确
                    }else {
                        _this.children('span').removeClass()
                        _this.children('span').addClass('glyphicon glyphicon-heart-empty')
                        _this.children('span').text('点赞'+data.number)
                        _this.children('span').attr('tag','0')
                    }
                })
            })
        })
    </script>
{% endblock %}